using System;
using System.Collections.Generic;
using System.Text;
using NBusiness.CodeDom;
using NBusiness.ESharp.Compiler;
using NBusiness.CodeDom.Compiler;
using System.Reflection;
using NBusiness.Data;

namespace NBusiness.ESharp
{
    public class ESharpValidation : EntityValidation
    {
        protected internal override void Attach()
        {
            foreach (EntityElement element in Elements)
            {
                int args = 0;
                switch (element.Type)
                {
                    case EntityElementType.ValidateProperty:
                        {
                            IValidatable[] found = (Parent as ESharpEntity).FindValidatable(element.Value);

                            if (found.Length == 0)
                                OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5001, element));
                            else if (found.Length > 1)
                                OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5005, element));
                            else
                                Validatable = found[0];
                        }
                        break;
                    case EntityElementType.ValidateType:
                        {                            
                            string ruleName = element.Value;
                            Type templateType = null;
                            if ((Parent.Parent as EntityFamily).Root.Using.ContainsKey(element.Path))
                            {
                                List<EntityUsing> usingStatements = new List<EntityUsing>((Parent.Parent as EntityFamily).Root.Using[element.Path]);
                                EntityUsing thisNamespace = new ESharpUsing();
                                thisNamespace.Namespace = (Parent.Parent as EntityFamily).Fullname;
                                thisNamespace.Root = (Parent.Parent as EntityFamily).Root;
                                usingStatements.Add(thisNamespace);
                                
                                foreach (EntityUsing eu in usingStatements)
                                {
                                    Type t = eu.FindType(element.Value, null);
                                    if (t == null)
                                        t = eu.FindType(element.Value + "Rule", null);

                                    if (t != null && templateType == null)
                                    {
                                        templateType = t;
                                    }
                                    else if (t != null && templateType != null && t != templateType)
                                    {
                                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5005, element));
                                        templateType = null;
                                        break;
                                    }
                                }
                            }

                            if (templateType != null)
                            {
                                ExpectedArgumentTypeAttribute expectedType = null;

                                object[] atts = templateType.GetCustomAttributes(typeof(ExpectedArgumentTypeAttribute), false);
                                if(atts.Length == 1)
                                {
                                    expectedType = atts[0] as ExpectedArgumentTypeAttribute;
                                }

                                if(expectedType != null)
                                {
                                    List<Type> argumentTypes = new List<Type>();
                                    foreach (PropertyInfo property in expectedType.ArgumentType.GetProperties())
                                    {
                                        if (property.IsDefined(typeof(ExpectedArgumentPropertyAttribute), false))
                                        {
                                            object [] expected = property.GetCustomAttributes(typeof(ExpectedArgumentPropertyAttribute), false);
                                            if ((expected[0] as ExpectedArgumentPropertyAttribute).ConverstionType != null)
                                                argumentTypes.Add((expected[0] as ExpectedArgumentPropertyAttribute).ConverstionType);
                                            else
                                                argumentTypes.Add(property.PropertyType);
                                        }
                                    }

                                    EntityValidationType validationType = new EntityValidationType(
                                        element.Value,
                                        templateType.FullName,
                                        templateType.Assembly.FullName,
                                        expectedType.ArgumentType.FullName,
                                        argumentTypes.ToArray());

                                    this.Type = validationType;
                                }
                                else OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5006, element, templateType.Name));
                            }
                            else OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5002, element));
                            break;
                        }
                        break;
                    case EntityElementType.ValidateArg:
                        if (Type != null)
                        {
                            if (Type.ArgumentParameters.Count > args)
                            {
                                System.Type t = Type.ArgumentParameters[args++];

                                bool valid = false;
                                try
                                {
                                    object obj = Convert.ChangeType(element.Value.Trim('"'), t);
                                    valid = true;
                                }
                                catch { }

                                if (!valid)
                                    OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5003, element));
                            }
                            else OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5004, element));
                        }
                        break;
                }
            }
        }

        protected override void InitializeAssignmentLogic()
        {
            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.Identifier,
                EntityElementType.ValidateProperty,
                EntityElementType.ValidateDeclaration));

            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.Identifier,
                EntityElementType.ValidateType,
                EntityElementType.ValidateProperty));

            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.ValidateArg,
                EntityElementType.ValidateArg,
                new AssignmentLogicHandler(AddArg),
                EntityElementType.ValidateType,
                EntityElementType.ValidateArgSplit));

            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.ValidateArgSplit,
                EntityElementType.ValidateArgSplit,
                EntityElementType.ValidateArg));

            Assignments.Add(new AssignmentLogic(
                ESharpParser.Expressions.StatementEnd,
                EntityElementType.StatementEnd,
                PostAssignmentAction.Stop,
                EntityElementType.ValidateArg,
                EntityElementType.ValidateType));
        }

        private void AddArg(object sender, AssignmentLogicEventArgs e)
        {
            Arguments.Add(e.Elements.Peek().Value.Trim('"'));
        }
    }
}
